package com.agentframework.examples.jinhua;

import com.agentframework.core.action.Action;
import com.agentframework.core.agent.AbstractAgent;
import com.agentframework.core.agent.AutoThinkReactStrategy;
import com.agentframework.core.annotation.Agent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.util.StringUtils;
import org.stringtemplate.v4.ST;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author wangjun
 **/
@Agent(
    name = "jinhuaDealerAgent",
    scope = Agent.Scope.SINGLETON,
    actions = {
        StartNewRoundAction.class,
        ViewCardsAction.class,
        BetAction.class,
        CompareAction.class,
        FoldAction.class
    }
)
public class JinhuaDealerAgent extends AbstractAgent {

    private static final Logger logger = LoggerFactory.getLogger(JinhuaDealerAgent.class);

    Map<String, Action> actionMap = new ConcurrentHashMap<>();

    String profile = """
             你是荷官，负责发牌、控制流程并宣布结果。请保持中立、公正、流程清晰。
                        
             游戏基本规则：
                 - 玩家初始三张暗牌，可选择"下注"（包括不看牌下注，即蒙牌）或"看牌"后行动
                 - 游戏按环形顺序轮流行动，直至只剩一名玩家或达成结束条件
                 - 玩家可选择操作：看牌、下注(跟注或加注)、比牌、弃牌
             
             其他规则：
                - 牌型由大到小：豹子 > 顺金 > 金花 > 顺子 > 对子 > 单张。
                - 蒙牌下注（不看牌）最低5倍，看牌后下注最低10倍，加注需大于当前最高下注。
                - 比牌时输家淘汰，弃牌玩家退出本轮游戏。
                        
             你的主要职责包括：
                 1. 初始化游戏，发牌给所有玩家（三张暗牌）
                 2. 按环形顺序引导玩家行动，例如提示"请选择操作：看牌、下注（跟注，加注）、比牌或弃牌"
                 3. **每轮结束时**宣布赢家与牌型，统计本轮输赢；所有轮次结束后，统计整体结果
                        
             你不能直接告诉玩家别人的牌，也不能参与下注。
                        
             当前轮到某位玩家时，请按以下规则引导：
                 - 如果玩家选择"看牌"，请回应："你已看牌，请继续选择【下注（跟注或加注）、比牌或弃牌】。"
                 - 玩家比牌时，若双方牌型相同则继续游戏，否则淘汰输家
                 - 玩家弃牌后，将其移出本轮行动顺序，但保留历史下注记录
                 
             玩家ID信息如下：%s    \n
             
             玩家游戏数据如下：   \n
             <players:{p | - ID: <p.playerId>, 本轮是否看牌: <p.viewed>, 本轮是否存活: <p.active> \\n}>
             
             游戏总共设置轮数: %s   \n
                        
             特别注意：
             - 如果玩家第一条消息指定下注，那么就直接下注，无需看牌，除非玩家明确在第一条消息中指定要看牌
             - 如果玩家的本轮是否看牌为true，表示已经看牌，不需要再调用viewCards工具处理玩家的看牌请求，直接调用bet工具处理下注请求即可
             - 玩家信息指玩家的唯一标识ID值，多个玩家以逗号分割，所有涉及到玩家的操作都需要传递玩家ID值
             - 第一个玩家下注的时候，下注金额只要不比最低下注金额5倍少就可以了
             - 玩家下注的时候，必须要指定下注金额是多少，如果玩家下注金额正确，直接调用bet下注工具处理下注请求即可，不需要再调用viewCards看牌工具处理玩家的看牌请求
             - 你需要根据玩家是否看牌下注，来判断玩家下注金额是否正确，比如上一个玩家看牌下注10倍，那么蒙牌下注只需要5倍或者大于5倍，如果是看牌下注，必须不能低于10倍
             - 如果玩家本轮已经看了牌，之后就不能再蒙牌，除非新的一轮游戏开始
             - 比牌后若双方牌型相同，那么比牌者输，被比牌者赢
             - 如果最后剩下2位玩家的情况下，他们连续下注超过10轮，可以提示玩家是否同意梭哈
            """;

    public JinhuaDealerAgent(JinhuaTool jinhuaTool) {
        super("荷官：JinhuaGameDealer");
        this.agentProfile = profile;
        this.setToolCallbacks(jinhuaTool);
        this.setReactModel(AutoThinkReactStrategy.class);
    }

    @Override
    protected AssistantMessage react() {
        Message message = getLatestMessage();
        if (message == null) {
            logger.error("jinhuaDealerAgent react() message is null");
            return null;
        }

        var action = (String) message.getMetadata().get("action");
        if (!StringUtils.hasText(action)) {
            throw new IllegalArgumentException("message metadata action is null，message：" + message.getText());
        }

        Action aaction = actionMap.get(action);
        if (aaction == null) {
            throw new IllegalArgumentException("message metadata action not found：" + action);
        }

        context.setTodo(aaction);
        return act();
    }

    @Override
    protected int observe() throws InterruptedException {

        int observe = super.observe();

        if (isGameOver()) {
            logger.debug("游戏已经结束，不再处理消息");
            return 0;
        }

        if (isRoundOver()) {
            boolean nextRound = ((JinhuaEnvironment) getEnvironment()).getJinhuaGameState().nextRound();
            if (nextRound) {
                logger.debug("本轮游戏只剩下一名玩家存活，需要开启新一轮游戏");
                UserMessage userMessage = new UserMessage("游戏开始");
                userMessage.getMetadata().put(SEND_TO, Set.of(getAgentId()));
                userMessage.getMetadata().put("action", "start_new_round");
                publishMessage(userMessage);
            } else {
                logger.debug(">>>游戏已经结束");
            }
            return 0;
        }

        Message message = getLatestMessage();
        // 添加调试日志
//        if (message != null) {
//            logger.debug(">>>荷官 处理消息: {}", message);
//        }
        
        // 根据消息的action_type选择对应的动作
        if (message != null && !message.getMetadata().containsKey("action")) {
            throw new IllegalArgumentException("message metadata action is null");
        }

        return observe;
    }

    private boolean isGameOver() {
        return ((JinhuaEnvironment) getEnvironment()).getJinhuaGameState().isGameOver();
    }

    protected boolean isRoundOver() {
        return ((JinhuaEnvironment) getEnvironment()).getJinhuaGameState().isRoundOver();
    }

    @Override
    public String getAgentProfile() {
        JinhuaGameState jinhuaGameState = ((JinhuaEnvironment) getContext().getEnvironment()).getJinhuaGameState();
        String playerIds = jinhuaGameState.getPlayerList().stream().map(PlayerInfo::getPlayerId).collect(Collectors.joining(","));

        List<JinhuaPlayerAgent.OtherPlayer> otherPlayers = jinhuaGameState.getPlayerList().stream()
                .map(p -> new JinhuaPlayerAgent.OtherPlayer(
                        p.getPlayerId(),
                        p.isViewed(),
                        p.isActive()))
                .collect(Collectors.toList());

        ST st = new ST(String.format(this.agentProfile, playerIds, jinhuaGameState.getTotalRounds()));
        st.add("players", otherPlayers);

        return st.render();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();
        actionMap.putAll(actions.stream().collect(Collectors.toMap(Action::name, action -> action)));
    }

}
